/*-------------------------------------------------------------------------
 *
 * primnodes.h
 *	  "原始" 节点类型的定义，这些类型在查询管道的解析/计划/执行阶段中使用超过一次。
 *	  目前，这些主要是可执行表达式和连接树的节点。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/nodes/primnodes.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef PRIMNODES_H
#define PRIMNODES_H

#include "access/attnum.h"
#include "nodes/bitmapset.h"
#include "nodes/pg_list.h"


/* ----------------------------------------------------------------
 *						节点定义
 * ----------------------------------------------------------------
 */

/*
 * 别名 -
 *	  为范围变量指定别名；别名也可能
 *	  指定表内列的重命名。
 *
 * 注意：colnames 是字符串节点的列表。在与 RTEs 关联的别名结构中，
 * 可能存在对应于被删除列的条目；这些通常是空字符串（""）。有关信息请参见 parsenodes.h。
 */
typedef struct Alias
{
	NodeTag		type;
	char	   *aliasname;		/* 别名关系名称（从不合格） */
	List	   *colnames;		/* 可选的列别名列表 */
} Alias;

/* 对于临时关系在提交时要做什么 */
typedef enum OnCommitAction
{
	ONCOMMIT_NOOP,				/* 没有 ON COMMIT 子句（什么也不做） */
	ONCOMMIT_PRESERVE_ROWS,		/* ON COMMIT PRESERVE ROWS（什么也不做） */
	ONCOMMIT_DELETE_ROWS,		/* ON COMMIT DELETE ROWS */
	ONCOMMIT_DROP				/* ON COMMIT DROP */
} OnCommitAction;

/*
 * RangeVar - 范围变量，用于 FROM 子句
 *
 * 还用于表示公用语句中的表名；在那里，别名
 * 字段不使用，inh 表示是否要递归地将操作
 * 应用于子表。在某些上下文中，在这里携带 TEMP 表指示符也是有用的。
 */
typedef struct RangeVar
{
	NodeTag		type;
	char	   *catalogname;	/* 目录（数据库）名称，或 NULL */
	char	   *schemaname;		/* 架构名称，或 NULL */
	char	   *relname;		/* 关系/序列名称 */
	bool		inh;			/* 通过继承扩展关系？递归作用于子节点？ */
	char		relpersistence; /* 见 pg_class.h 中的 RELPERSISTENCE_* */
	Alias	   *alias;			/* 表别名和可选的列别名 */
	int			location;		/* 令牌位置，如果未知则为-1 */
} RangeVar;


/*
 * TableFunc - 表示一个表函数的节点，例如 XMLTABLE。
 *
 * ns_names 列表中的条目可以是包含文字命名空间名称的字符串节点，或者是表示 DEFAULT 的 NULL 指针。
 */
typedef struct TableFunc
{
	NodeTag		type;
	List	   *ns_uris;		/* 命名空间 URI 表达式列表 */
	List	   *ns_names;		/* 命名空间名称或 NULL 列表 */
	Node	   *docexpr;		/* 输入文档表达式 */
	Node	   *rowexpr;		/* 行过滤器表达式 */
	List	   *colnames;		/* 列名称（字符串列表） */
	List	   *coltypes;		/* 列类型 OIDs 的 OID 列表 */
	List	   *coltypmods;		/* 列 typmods 的整数列表 */
	List	   *colcollations;	/* 列 collation OIDs 的 OID 列表 */
	List	   *colexprs;		/* 列过滤器表达式列表 */
	List	   *coldefexprs;	/* 列默认表达式列表 */
	Bitmapset  *notnulls;		/* 每个输出列的可空性标志 */
	int			ordinalitycol;	/* 计数从 0 开始；如果未指定，则为 -1 */
	int			location;		/* 令牌位置，如果未知则为-1 */
} TableFunc;

/*
 * IntoClause - SELECT INTO、CREATE TABLE AS 和 CREATE MATERIALIZED VIEW 的目标信息
 *
 * 对于 CREATE MATERIALIZED VIEW，viewQuery 是针对视图的解析但未重写的 SELECT 查询；
 * 否则为 NULL。（尽管它实际上是 Query*，我们将其声明为 Node* 以避免前向引用。）
 */
typedef struct IntoClause
{
	NodeTag		type;

	RangeVar   *rel;			/* 目标关系名称 */
	List	   *colNames;		/* 要分配的列名，或 NIL */
	char	   *accessMethod;	/* 表访问方法 */
	List	   *options;		/* 来自 WITH 子句的选项 */
	OnCommitAction onCommit;	/* 在 COMMIT 时我们要做什么？ */
	char	   *tableSpaceName; /* 要使用的表空间，或 NULL */
	Node	   *viewQuery;		/* 物化视图的 SELECT 查询 */
	bool		skipData;		/* 对于 WITH NO DATA 为 true */
} IntoClause;


/* ----------------------------------------------------------------
 *					可执行表达式的节点类型
 * ----------------------------------------------------------------
 */

/*
 * Expr - 可执行表达式节点的通用超类
 *
 * 所有在可执行表达式树中使用的节点类型都应该继承自 Expr（即，具有 Expr
 * 作为它们的第一个字段）。由于 Expr 仅包含 NodeTag，这是一种形式上的规定，但它是一种简易的文档形式。
 * 另见 execnodes.h 中的 ExprState 节点类型。
 */
typedef struct Expr
{
	NodeTag		type;
} Expr;

/*
 * Var - 表示变量的表达式节点（即，表列）
 *
 * 在解析器和规划器中，varno 和 varattno 确定语义
 * 参考对象，它是一个基本关系列，除非参考是指非语义
 * 等价于任一连接输入列的连接 USING 列
 * （因为这是一个 FULL 连接或输入列需要类型强制转换）。
 * 在这些情况下，varno 和 varattno 指的是 JOIN RTE。 （在规划的早期，
 * 我们用隐含表达式替换此类连接引用；但在那之前，我们希望连接引用
 * Vars 保持其原始身份以便于查询打印。）
 *
 * 在规划结束时，出现在上层计划节点中的 Var 节点被重新分配，
 * 指向其子计划的输出；例如，在连接节点中，varno 变为 INNER_VAR
 * 或 OUTER_VAR，varattno 变为该子计划目标列表的适当元素的索引。
 * 类似地，INDEX_VAR 用于标识引用索引列而不是堆列的 Vars。
 * （在 ForeignScan 和 CustomScan 计划节点中，INDEX_VAR 被滥用
 * 来表示对自定义扫描元组类型列的引用。）
 *
 * ROWID_VAR 用于规划器中标识携带行身份信息的临时变量
 * 在 UPDATE/DELETE/MERGE 期间。这个值在规划器外部不应该被看到。
 *
 * 在解析器中，varnosyn 和 varattnosyn 要么与
 * varno/varattno 相同，要么指定在别名 JOIN
 * RTE 中列的位置信息，该 RTE 隐藏了语义参考对象 RTE 的
 * refname。这是一个语法标识符，而不是语义标识符；它告诉 ruleutils.c
 * 如何正确打印 Var。varnosyn/varattnosyn 在整个规划和执行过程中
 * 保持其值，因此在调试时特别有助于识别 Vars。
 * 然而，请注意，在规划器中生成的 Var 如果
 * 不对应于任何简单关系列，则可能有
 * varnosyn = varattnosyn = 0。
 */
#define    INNER_VAR		(-1)	/* 对内部子计划的引用 */
#define    OUTER_VAR		(-2)	/* 对外部子计划的引用 */
#define    INDEX_VAR		(-3)	/* 对索引列的引用 */
#define    ROWID_VAR		(-4)	/* 规划期间的行身份列 */

#define IS_SPECIAL_VARNO(varno)		((int) (varno) < 0)

/* 规则中特殊 RTE 条目的索引的符号 */
#define    PRS2_OLD_VARNO			1
#define    PRS2_NEW_VARNO			2

typedef struct Var
{
	Expr		xpr;
	int			varno;			/* 此变量在范围中的关系索引
								 * 表，或 INNER_VAR/OUTER_VAR 等 */
	AttrNumber	varattno;		/* 此变量的属性编号，或零表示
								 * 所有属性（"整个行 Var"） */
	Oid			vartype;		/* 此变量的 pg_type OID */
	int32		vartypmod;		/* pg_attribute typmod 值 */
	Oid			varcollid;		/* 排序规则的 OID，如果没有则为 InvalidOid */
	Index		varlevelsup;	/* 用于引用外部关系的子查询变量；
								 * 在普通变量中为 0，>0 代表向上 N
								 * 层级 */
	Index		varnosyn;		/* 语法关系索引（如果未知则为 0） */
	AttrNumber	varattnosyn;	/* 语法属性编号 */
	int			location;		/* 令牌位置，如果未知则为-1 */
} Var;

/*
 * Const
 *
 * 注意：对于 varlena 数据类型，我们规定 Const 节点的值
 * 必须是非扩展形式（4 字节头，无压缩或外部
 * 引用）。这确保 Const 节点是自包容的，并使
 * equal() 更可能将逻辑上相同的值视为相等。
 */
typedef struct Const
{
	Expr		xpr;
	Oid			consttype;		/* 常量数据类型的 pg_type OID */
	int32		consttypmod;	/* typmod 值，如果有的话 */
	Oid			constcollid;	/* 排序规则的 OID，如果没有则为 InvalidOid */
	int			constlen;		/* 常量数据类型的 typlen */
	Datum		constvalue;		/* 常量的值 */
	bool		constisnull;	/* 常量是否为 null（如果为 true，
								 * constvalue 是未定义的） */
	bool		constbyval;		/* 此数据类型是否按值传递。
								 * 如果为 true，则所有信息存储
								 * 在 Datum 中。如果为 false，则 Datum
								 * 包含指向信息的指针。 */
	int			location;		/* 令牌位置，如果未知则为-1 */
} Const;


/*
 * Param
 *
 *		paramkind 指定参数的类型。该字段的可能值为：
 *
 *		PARAM_EXTERN:  参数值来自计划之外。
 *				此类参数编号从 1 到 n。
 *
 *		PARAM_EXEC:  参数是一个内部执行器参数，用于在子查询之间传递值，
 *				或从嵌套循环连接到其内部扫描。
 *				出于历史原因，此类参数的编号从 0 开始。
 *				这些数字独立于 PARAM_EXTERN 数字。
 *
 *		PARAM_SUBLINK:	参数表示 SubLink 节点子选择的输出列。
 *				列编号包含在 `paramid' 字段中。
 *				（这种类型的 Param 在规划过程中转换为 PARAM_EXEC。）
 *
 *		PARAM_MULTIEXPR:  类似于 PARAM_SUBLINK，参数表示 SubLink 节点子选择的输出列，
 *				但在这里，SubLink 始终是一个 MULTIEXPR SubLink。
 *				`paramid' 字段的高 16 位包含 SubLink 的 subLinkId，
 *				低 16 位包含列编号。
 *				（这种类型的 Param 在规划过程中也转换为 PARAM_EXEC。）
 */
typedef enum ParamKind
{
	PARAM_EXTERN,
	PARAM_EXEC,
	PARAM_SUBLINK,
	PARAM_MULTIEXPR
} ParamKind;

typedef struct Param
{
	Expr		xpr;
	ParamKind	paramkind;		/* 参数的类型。见上文 */
	int			paramid;		/* 参数的数字 ID */
	Oid			paramtype;		/* 参数数据类型的 pg_type OID */
	int32		paramtypmod;	/* typmod 值（如果已知） */
	Oid			paramcollid;	/* 排序规则的 OID，如果没有则为 InvalidOid */
	int			location;		/* 令牌位置，如果未知则为-1 */
} Param;

/*
 * Aggref
 *
 * 聚合的参数列表是一个目标列表，即一组TargetEntry节点。
 *
 * 对于正常的（非有序集）聚合，非resjunk的TargetEntries
 * 表示聚合的常规参数（如果有的话），并且可以在最后添加resjunk TLEs
 * 以表示不是参数的ORDER BY表达式。与顶层查询一样，
 * TLEs可以标记为ressortgroupref索引，以便可以通过SortGroupClause
 * 条目在aggorder和/或aggdistinct列表中引用它们。这表示在
 * 将它们传递给过渡函数之前，要对聚合输入行应用的ORDER BY
 * 和DISTINCT操作。语法只允许一个简单的“DISTINCT”说明符来
 * 表示参数，但我们使用完整的查询级表示法以允许更多的代码共享。
 *
 * 对于有序集聚合，args列表表示GROUP内（聚合）参数，
 * 所有这些参数都将在aggorder列表中列出。在这种情况下，
 * 不支持DISTINCT，因此aggdistinct将为NIL。
 * 直接参数出现在aggdirectargs中（作为普通表达式的列表，
 * 而不是TargetEntry节点）。
 *
 * aggtranstype是该聚合的状态转换值的数据类型（在agg的transtype为
 * 多态时解析为实际类型）。这一点在计划期间确定，在此之前为
 * InvalidOid。
 *
 * aggargtypes是直接和常规参数的数据类型的OID列表。通常，这与
 * aggdirectargs和args列表是多余的，但在组合聚合中，这是不一样的，
 * 因为args列表已被替换为代表部分聚合过渡值的单个参数。
 *
 * aggsplit指示Aggref的父计划节点的预期部分聚合模式。在解析器中，
 * 它始终设置为AGGSPLIT_SIMPLE，但规划者可能会将其更改为其他值。
 * 我们主要使用这个作为一个交叉检查，以确保Aggrefs与计划匹配；
 * 但请注意，当aggsplit指示非最终模式时，aggtype反映的是
 * 过渡数据类型，而不是聚合的SQL级输出类型。
 *
 * aggno和aggtransno在解析阶段为-1，并在规划中设置。
 * 拥有相同'aggno'的聚合表示相同的聚合表达式，
 * 并可以共享结果。拥有相同'transno'但不同'aggno'的聚合
 * 可以共享相同的过渡状态，只有最终函数需要单独调用。
 */
typedef struct Aggref
{
	Expr		xpr;
	Oid			aggfnoid;		/* pg_proc 聚合的 Oid */
	Oid			aggtype;		/* 聚合结果的类型Oid */
	Oid			aggcollid;		/* 结果的排序规则的OID */
	Oid			inputcollid;	/* 函数应使用的排序规则的OID */
	Oid			aggtranstype;	/* 聚合的过渡值的类型Oid */
	List	   *aggargtypes;	/* 直接和聚合参数的类型Oid */
	List	   *aggdirectargs;	/* 直接参数，如果是有序集聚合 */
	List	   *args;			/* 聚合参数和排序表达式 */
	List	   *aggorder;		/* ORDER BY（SortGroupClause列表） */
	List	   *aggdistinct;	/* DISTINCT（SortGroupClause列表） */
	Expr	   *aggfilter;		/* FILTER表达式（如果有） */
	bool		aggstar;		/* 如果参数列表确实是'*'则为true */
	bool		aggvariadic;	/* 如果变参参数已合并为最后一个数组参数则为true */
	char		aggkind;		/* 聚合类型（见pg_aggregate.h） */
	Index		agglevelsup;	/* 如果agg属于外部查询则大于0 */
	AggSplit	aggsplit;		/* 父Agg的预期agg拆分模式 */
	int			aggno;			/* Agg节点内的唯一ID */
	int			aggtransno;		/* Agg的过渡状态的唯一ID */
	int			location;		/* 令牌位置，如果未知则为-1 */
} Aggref;

/*
 * GroupingFunc
 *
 * GroupingFunc是一个GROUPING(...)表达式，在许多方面
 * 类似于聚合函数（例如，它“属于”一个特定的查询级别，
 * 这可能不是立即包含它的那个级别），但在一个重要的方面又有所不同：
 * 它从不计算其参数，它们仅仅指定表达式来自于
 * 它所属的查询级别的GROUP BY子句。
 *
 * 规范仅通过语法替换定义GROUPING()的评估，
 * 但为了优化的目的，我们将其作为一个真正的表达式，
 * 以便一个Agg节点可以同时处理多个分组集。评估结果仅需要
 * 检查与正在投影的分组集相对的列位置。
 * 但是，为了使EXPLAIN生成有意义的输出，我们必须保留
 * 原始表达式，因为表达式反解析并没有为我们提供
 * 访问GROUP BY子句的可行方法。
 *
 * 此外，如果两个GroupingFunc节点具有相等的参数列表
 * 和agglevelsup，则将它们视为相等，而不比较refs和cols注释。
 *
 * 在原始解析输出中，我们只有args列表；解析分析填充
 * refs列表，规划者填充cols列表。
 */
typedef struct GroupingFunc
{
	Expr		xpr;
	List	   *args;			/* 参数，未评估，但保留以便于
 * EXPLAIN等。 */
	List	   *refs;			/* 参数的ressortgrouprefs */
	List	   *cols;			/* 计划程序设置的实际列位置 */
	Index		agglevelsup;	/* 与 Aggref.agglevelsup 相同 */
	int			location;		/* 令牌位置 */
} GroupingFunc;

/*
 * 窗口函数
 */
typedef struct WindowFunc
{
	Expr		xpr;
	Oid			winfnoid;		/* 函数的 pg_proc Oid */
	Oid			wintype;		/* 窗口函数结果的类型 Oid */
	Oid			wincollid;		/* 结果的排序规则的OID */
	Oid			inputcollid;	/* 函数应使用的排序规则的OID */
	List	   *args;			/* 窗口函数的参数 */
	Expr	   *aggfilter;		/* FILTER表达式（如果有） */
	Index		winref;			/* 关联 WindowClause 的索引 */
	bool		winstar;		/* 如果参数列表确实是'*'则为true */
	bool		winagg;			/* 函数是简单的聚合吗？ */
	int			location;		/* 令牌位置，如果未知则为-1 */
} WindowFunc;

/*
 * SubscriptingRef：描述对容器（数组等）的下标操作。
 *
 * SubscriptingRef 可以描述从容器中获取单个元素，
 * 获取容器的一部分（例如，数组切片），将单个元素存储到容器中，
 * 或存储一个切片。 “存储”情况处理初始容器值和插入到容器适当部分的源值；
 * 操作的结果是整个新的修改后的容器值。
 *
 * 如果 reflowerindexpr = NIL，则我们在给定的 refupperindexpr 下标中获取或存储单个容器元素。
 * 否则，我们在获取或存储一个容器切片，也就是一个矩形子容器，
 * 其下限和上限由索引表达式给出。
 * 当 reflowerindexpr 不为 NIL 时，其长度必须与 refupperindexpr 相同。
 *
 * 在切片情况下，下标列表中的个别表达式可以为 NULL，表示“替代数组的当前下限或上限”。
 * （非数组容器可能支持或不支持此功能。）
 *
 * refcontainertype 是实际的容器类型，决定下标的语义。
 * （这通常是 refexpr 的暴露类型，或者如果是域则是基本类型。）
 * refelemtype 是容器元素的类型；这被保存用于下标函数的使用，但不被核心代码使用。
 * refrestype、reftypmod 和 refcollid 描述 SubscriptingRef 结果的类型。
 * 在存储表达式中，refrestype 会始终与 refcontainertype 匹配；在获取中，
 * 它可以是元素获取的 refelemtype，或切片获取的 refcontainertype，或可能是由特定类型下标逻辑确定的其他东西。
 * 同样，reftypmod 和 refcollid 在存储时会与容器的属性匹配，但在获取时可能不同。
 *
 * 注意：对于返回容器的情况，如果 refexpr 产生一个 R/W 扩展容器，则允许实现就地修改该对象并返回相同的对象。
 */
typedef struct SubscriptingRef
{
	Expr		xpr;
	Oid			refcontainertype;	/* 容器本身的类型 */
	Oid			refelemtype;	/* 容器类型的 pg_type.typelem */
	Oid			refrestype;		/* SubscriptingRef 结果的类型 */
	int32		reftypmod;		/* 结果的 typmod */
	Oid			refcollid;		/* 结果的排序规则，或如果没有则为 InvalidOid */
	List	   *refupperindexpr;	/* 计算为上限的表达式
									 * 容器索引 */
	List	   *reflowerindexpr;	/* 计算为下限的表达式
									 * 容器索引，或单个容器元素的 NIL */
	Expr	   *refexpr;		/* 计算为容器值的表达式 */
	Expr	   *refassgnexpr;	/* 源值的表达式，或如果
								 * 获取则为 NULL */
} SubscriptingRef;

/*
 * CoercionContext - 区分允许的类型转换集合
 *
 * 注意：替代方案的顺序是重要的；后面的（更大的）值
 * 允许的转换比前面的更多。
 */
typedef enum CoercionContext
{
	COERCION_IMPLICIT,			/* 表达式上下文中的强制转换 */
	COERCION_ASSIGNMENT,		/* 赋值上下文中的强制转换 */
	COERCION_PLPGSQL,			/* 如果没有赋值转换，则使用 CoerceViaIO */
	COERCION_EXPLICIT			/* 显式强制转换操作 */
} CoercionContext;

/*
 * CoercionForm - 如何显示 FuncExpr 或相关节点
 *
 * “Coercion”有点不准确，因为这个值记录了除了转换之外的其他
 * 特殊语法，但现在我们将保留这个命名。
 *
 * 注意：equal() 忽略 CoercionForm 字段，因此这 *必须* 不携带
 * 任何有语义意义的信息。我们需要这种行为，以便
 * 规划者会将等效的隐式和显式转换视为等效。在那些确实表现不同的情况下，
 * 转换函数的参数将不同。
 */
typedef enum CoercionForm
{
	COERCE_EXPLICIT_CALL,		/* 作为函数调用显示 */
	COERCE_EXPLICIT_CAST,		/* 作为显式转换显示 */
	COERCE_IMPLICIT_CAST,		/* 隐式转换，因此隐藏它 */
	COERCE_SQL_SYNTAX			/* 使用 SQL 强制的特殊语法显示 */
} CoercionForm;

/*
 * FuncExpr - 函数调用的表达式节点
 */
typedef struct FuncExpr
{
	Expr		xpr;
	Oid			funcid;			/* 函数的 PG_PROC OID */
	Oid			funcresulttype; /* 结果值的 PG_TYPE OID */
	bool		funcretset;		/* 如果函数返回集合则为 true */
	bool		funcvariadic;	/* 如果变参参数已合并为最后一个数组参数则为true */
	CoercionForm funcformat;	/* 如何显示此函数调用 */
	Oid			funccollid;		/* 结果的排序规则的OID */
	Oid			inputcollid;	/* 函数应使用的排序规则的OID */
	List	   *args;			/* 函数的参数 */
	int			location;		/* 令牌位置，如果未知则为-1 */
} FuncExpr;

/*
 * NamedArgExpr - 函数的命名参数
 *
 * 此节点类型只能出现在 FuncCall 或 FuncExpr
 * 节点的参数列表中。我们支持纯位置调用表示法（没有命名参数），
 * 命名表示法（所有参数都有名称），以及混合表示法（无名
 * 参数后跟命名参数）。
 *
 * 解析分析将 argnumber 设置为参数的位置索引，
 * 但不会重新排列参数列表。
 *
 * 规划者将在表达式预处理期间将参数列表转换为纯位置表示法，
 * 因此执行永远看不到 NamedArgExpr。
 */
typedef struct NamedArgExpr
{
	Expr		xpr;
	Expr	   *arg;			/* 参数表达式 */
	char	   *name;			/* the name */
	int			argnumber;		/* 位置表示法中参数的编号 */
	int			location;		/* 参数名称位置，如果未知则为 -1 */
} NamedArgExpr;


typedef struct OpExpr
{
	Expr		xpr;
	Oid			opno;			/* 运算符的 PG_OPERATOR OID */
	Oid			opfuncid;		/* 基础函数的 PG_PROC OID */
	Oid			opresulttype;	/* 结果值的 PG_TYPE OID */
	bool		opretset;		/* true if operator returns set */
	Oid			opcollid;		/* 结果的排序规则的OID */
	Oid			inputcollid;	/* 运算符应使用的排序规则的OID */
	List	   *args;			/* 运算符的参数（1 或 2） */
	int			location;		/* 令牌位置，如果未知则为-1 */
} OpExpr;

/*
 * DistinctExpr - "x IS DISTINCT FROM y" 的表达式节点
 *
 * 除了 nodetag，这与引用 "=" 运算符的 OpExpr 表达方式相同。
 * 我们使用 "="，而不是更明显的 "<>"，因为更多的数据类型有 "="
 * 而不是 "<>"。这意味着执行器必须对运算符结果进行反转。
 * 注意，如果任一输入为 NULL，则根本不会调用运算符函数，因为此时可以直接确定结果。
 */
typedef OpExpr DistinctExpr;

/*
 * NullIfExpr - NULLIF 表达式
 *
 * 如同 DistinctExpr，这与引用 "=" 运算符的 OpExpr 的表示方式相同。
 */
typedef OpExpr NullIfExpr;

/*
 * ScalarArrayOpExpr - "标量运算符 ANY/ALL (数组)" 的表达式节点
 *
 * 运算符必须返回布尔值。它应用于左操作数
 * 和右侧数组的每个元素，结果通过 OR 或 AND（分别对于 ANY 或 ALL）组合。
 * 节点表示几乎与基础运算符相同，但我们需要一个 useOr
 * 标志来记住它是 ANY 还是 ALL，并且我们不必存储
 * 结果类型（或排序规则），因为它必须是布尔的。
 *
 * 带有有效 hashfuncid 的 ScalarArrayOpExpr 在执行期间
 * 通过构建一个包含来自 RHS 参数的 Const 值的哈希表进行评估。
 * 此表在表达式评估期间被探测。当计划程序将
 * hashfuncid 设置为必须用于构建和探测哈希表的哈希函数时。
 * 执行器决定是否应使用基于哈希的检查或
 * 更传统的方式，取决于 hashfuncid 是否已设置。
 *
 * 在执行哈希 NOT IN 时，negfuncid 也将被设置为
 * 哈希表必须使用的等价函数来构建和探测哈希表。
 * opno 和 opfuncid 将保持设置为 <> 运算符及其
 * 相应的函数，并且在执行期间不会使用。对于
 * 非哈希表基础的 NOT IN，negfuncid 将设置为 InvalidOid。
 * 请参阅 convert_saop_to_hashed_saop()。
 */
typedef struct ScalarArrayOpExpr
{
	Expr		xpr;
	Oid			opno;			/* 运算符的 PG_OPERATOR OID */
	Oid			opfuncid;		/* 比较函数的 PG_PROC OID */
	Oid			hashfuncid;		/* 哈希函数的 PG_PROC OID 或 InvalidOid */
	Oid			negfuncid;		/* opfuncid 函数的否定者的 PG_PROC OID
								 * 或 InvalidOid。见上文 */
	bool		useOr;			/* 对于 ANY 为真，对于 ALL 为假 */
	Oid			inputcollid;	/* 运算符应使用的排序规则的OID */
	List	   *args;			/* 标量和数组操作数 */
	int			location;		/* 令牌位置，如果未知则为-1 */
} ScalarArrayOpExpr;

/*
 * BoolExpr - 基本布尔运算符 AND, OR, NOT 的表达式节点
 *
 * 注意，参数以 List 形式给出。对于 NOT，当然列表
 * 必须始终恰好有一个元素。对于 AND 和 OR，可以有两个
 * 或更多参数。
 */
typedef enum BoolExprType
{
	AND_EXPR, OR_EXPR, NOT_EXPR
} BoolExprType;

typedef struct BoolExpr
{
	Expr		xpr;
	BoolExprType boolop;
	List	   *args;			/* 该表达式的参数 */
	int			location;		/* 令牌位置，如果未知则为-1 */
} BoolExpr;


/*
 * SubLink
 *
 * SubLink表示出现在表达式中的子选择，有时也表示其上方的组合运算符。subLinkType指示所表示的表达式的形式：
 *	EXISTS_SUBLINK		EXISTS(SELECT ...)
 *	ALL_SUBLINK			(lefthand) op ALL (SELECT ...)
 *	ANY_SUBLINK			(lefthand) op ANY (SELECT ...)
 *	ROWCOMPARE_SUBLINK	(lefthand) op (SELECT ...)
 *	EXPR_SUBLINK		(SELECT with single targetlist item ...)
 *	MULTIEXPR_SUBLINK	(SELECT with multiple targetlist items ...)
 *	ARRAY_SUBLINK		ARRAY(SELECT with single targetlist item ...)
 *	CTE_SUBLINK			WITH query (实际上从不是表达式的一部分)
 * 对于ALL、ANY和ROWCOMPARE，lefthand是与子选择的目标列表长度相同的表达式列表。ROWCOMPARE *总是* 具有多个条目的列表；如果子选择只有一个目标，则解析器将创建EXPR_SUBLINK（并且子选择上方的任何运算符将单独表示）。
 * ROWCOMPARE、EXPR和MULTIEXPR要求子选择最多返回一行（如果返回零行，则结果为NULL）。
 * ALL、ANY和ROWCOMPARE要求组合运算符返回布尔结果。ALL和ANY分别使用AND和OR语义组合逐行结果。
 * ARRAY仅需要一个目标列，并使用从子选择中生成的任意数量的行创建目标列类型的数组。
 *
 * SubLink被归类为Expr节点，但实际上不可执行；在规划期间，它必须在表达树中被SubPlan节点替换。
 *
 * 注意：在gram.y的原始输出中，testexpr仅包含lefthand表达式的原始形式（如果有），operName是组合运算符的字符串名称。此外，subselect是原始解析树。在解析分析期间，解析器将testexpr转换为完整的布尔表达式，该表达式将lefthand值与表示子选择输出列的PARAM_SUBLINK节点进行比较。而subselect则被转换为查询。这是保存在规则和重写器中看到的表示。
 *
 * 在EXISTS、EXPR、MULTIEXPR和ARRAY SubLinks中，testexpr和operName未使用，且始终为null。
 *
 * subLinkId目前仅用于MULTIEXPR SubLinks，其它SubLinks中为零。该数字标识UPDATE语句的SET列表中的不同多重赋值子查询。它在特定目标列表中是唯一的。MULTIEXPR的输出列由出现在tlist中的PARAM_MULTIEXPR参数引用。
 *
 * CTE_SUBLINK情况在实际的SubLink节点中从未出现，但在为WITH子查询生成的SubPlans中使用。
 */
typedef enum SubLinkType
{
	EXISTS_SUBLINK,
	ALL_SUBLINK,
	ANY_SUBLINK,
	ROWCOMPARE_SUBLINK,
	EXPR_SUBLINK,
	MULTIEXPR_SUBLINK,
	ARRAY_SUBLINK,
	CTE_SUBLINK					/* 仅适用于SubPlans */
} SubLinkType;


typedef struct SubLink
{
	Expr		xpr;
	SubLinkType subLinkType;	/* 详见上文 */
	int			subLinkId;		/* ID（1..n）；如果不是MULTIEXPR则为0 */
	Node	   *testexpr;		/* ALL/ANY/ROWCOMPARE的外部查询测试 */
	List	   *operName;		/* 原始指定的运算符名称 */
	Node	   *subselect;		/* 作为Query*或原始解析树的子选择 */
	int			location;		/* 令牌位置，如果未知则为-1 */
} SubLink;

/*
 * SubPlan - 可执行表达式节点，用于子计划（子查询）
 *
 * 规划器在完成子查询的规划后，用 SubPlan 节点替换表达式树中的 SubLink 节点。SubPlan 引用存储在顶层 PlannedStmt 的 subplans 列表中的 sub-plantree。
 * （我们避免直接链接，以便于复制表达式树而不会导致子计划的多次处理。）
 *
 * 在普通子计划中，testexpr 指向可执行表达式（OpExpr，OpExprs 的 AND/OR 树或 RowCompareExpr），用于组合操作符；左侧参数是原始左侧表达式，
 * 右侧参数是代表子查询输出的 PARAM_EXEC Param 节点。（注意：运行时可能还会插入强制转换函数。）这与原始 SubLink 节点中的 testexpr 是相同的表达式树，
 * 但 PARAM_SUBLINK 节点被适当编号的 PARAM_EXEC 节点替换。
 *
 * 如果子查询变为 initplan 而不是子计划，可执行表达式作为外部计划的表达式树的一部分（而 SubPlan 节点本身则不在其中，而是位于外部计划的 initPlan 列表中）。
 * 在这种情况下，为避免重复，testexpr 为 NULL。
 *
 * 规划器还推导出需传入和传出子计划的值列表。输入值表示为一组将在外部查询上下文中评估的表达式列表 "args"（当前这些 args 始终只是 Vars，
 * 但原则上它们可以是任何表达式）。这些值被分配给由 parParam 索引的全局 PARAM_EXEC 参数（parParam 和 args 列表必须具有相同的顺序）。
 * setParam 是子查询计算出的 PARAM_EXEC 参数的列表，如果它是 initplan 或 MULTIEXPR 计划的话；它们按子查询输出列的位置顺序列出。
 * （parParam 和 setParam 是整数列表，而不是 Bitmapsets，因为它们的顺序是重要的。）
 *
 * 此外，规划器计算使用 SubPlan 的启动成本和每调用成本。请注意，这些成本包括子查询本身的成本，
 * 以及需要评估的 testexpr（如果有）和任何哈希表管理开销。
 */
typedef struct SubPlan
{
	Expr		xpr;
	/* 从原始 SubLink 复制的字段： */
	SubLinkType subLinkType;	/* 详见上文 */
	/* 组合操作符，转换为可执行表达式： */
	Node	   *testexpr;		/* OpExpr 或 RowCompareExpr 表达式树 */
	List	   *paramIds;		/* 嵌入上述的 Params 的 ID */
	/* 要使用的计划树的标识： */
	int			plan_id;		/* Index (from 1) in PlannedStmt.subplans */
	/* Identification of the SubPlan for EXPLAIN and debugging purposes: */
	char	   *plan_name;		/* A name assigned during planning */
	/* Extra data useful for determining subplan's output type: */
	Oid			firstColType;	/* Type of first column of subplan result */
	int32		firstColTypmod; /* Typmod of first column of subplan result */
	Oid			firstColCollation;	/* Collation of first column of subplan
									 * result */
	/* Information about execution strategy: */
	bool		useHashTable;	/* true to store subselect output in a hash
								 * table (implies we are doing "IN") */
	bool		unknownEqFalse; /* true if it's okay to return FALSE when the
								 * spec result is UNKNOWN; this allows much
								 * simpler handling of null values */
	bool		parallel_safe;	/* is the subplan parallel-safe? */
	/* Note: parallel_safe does not consider contents of testexpr or args */
	/* Information for passing params into and out of the subselect: */
	/* setParam and parParam are lists of integers (param IDs) */
	List	   *setParam;		/* initplan and MULTIEXPR subqueries have to
								 * set these Params for parent plan */
	List	   *parParam;		/* indices of input Params from parent plan */
	List	   *args;			/* exprs to pass as parParam values */
	/* Estimated execution costs: */
	Cost		startup_cost;	/* one-time setup cost */
	Cost		per_call_cost;	/* cost for each subplan evaluation */
} SubPlan;

/*
 * AlternativeSubPlan - expression node for a choice among SubPlans
 *
 * This is used only transiently during planning: by the time the plan
 * reaches the executor, all AlternativeSubPlan nodes have been removed.
 *
 * The subplans are given as a List so that the node definition need not
 * change if there's ever more than two alternatives.  For the moment,
 * though, there are always exactly two; and the first one is the fast-start
 * plan.
 */
typedef struct AlternativeSubPlan
{
	Expr		xpr;
	List	   *subplans;		/* SubPlan(s) with equivalent results */
} AlternativeSubPlan;

/* ----------------
 * FieldSelect
 *
 * FieldSelect represents the operation of extracting one field from a tuple
 * value.  At runtime, the input expression is expected to yield a rowtype
 * Datum.  The specified field number is extracted and returned as a Datum.
 * ----------------
 */

typedef struct FieldSelect
{
	Expr		xpr;
	Expr	   *arg;			/* 输入表达式 */
	AttrNumber	fieldnum;		/* attribute number of field to extract */
	Oid			resulttype;		/* type of the field (result type of this
								 * node) */
	int32		resulttypmod;	/* output typmod (usually -1) */
	Oid			resultcollid;	/* 字段的排序类型的OID */
} FieldSelect;

/* ----------------
 * FieldStore
 *
 * FieldStore表示修改元组值中的一个字段的操作，产生一个新的元组值（输入不受影响！）。与SubscriptingRef的赋值情况类似，这用于实现对列部分的UPDATE。
 *
 * resulttype始终是一个命名复合类型（不是域）。要更新复合域值，请将CoerceToDomain应用于FieldStore。
 *
 * 单个FieldStore实际上可以表示几个不同字段的更新。解析器仅生成具有单元素列表的FieldStores，但规划器将会将同一基列的多个更新归并为一个FieldStore。
 * ----------------
 */

typedef struct FieldStore
{
	Expr		xpr;
	Expr	   *arg;			/* 输入元组值 */
	List	   *newvals;		/* 新字段的值 */
	List	   *fieldnums;		/* 整数列表的字段 attnums */
	Oid			resulttype;		/* type of result (same as type of arg) */
	/* Like RowExpr, we deliberately omit a typmod and collation here */
} FieldStore;

/* ----------------
 * RelabelType
 *
 * RelabelType represents a "dummy" type coercion between two binary-
 * compatible datatypes, such as reinterpreting the result of an OID
 * expression as an int4.  It is a no-op at runtime; we only need it
 * to provide a place to store the correct type to be attributed to
 * the expression result during type resolution.  (We can't get away
 * with just overwriting the type field of the input expression node,
 * so we need a separate node to show the coercion's result type.)
 * ----------------
 */

typedef struct RelabelType
{
	Expr		xpr;
	Expr	   *arg;			/* 输入表达式 */
	Oid			resulttype;		/* 强制转换表达式的输出类型 */
	int32		resulttypmod;	/* output typmod (usually -1) */
	Oid			resultcollid;	/* 排序规则的 OID，如果没有则为 InvalidOid */
	CoercionForm relabelformat; /* 如何显示这个节点 */
	int			location;		/* 令牌位置，如果未知则为-1 */
} RelabelType;

/* ----------------
 * CoerceViaIO
 *
 * CoerceViaIO 表示两种文本表示兼容的类型之间的类型强制， 
 * 通过调用源类型的 typoutput 函数然后是目标类型的 typinput 函数来实现。
 * ----------------
 */

typedef struct CoerceViaIO
{
	Expr		xpr;
	Expr	   *arg;			/* 输入表达式 */
	Oid			resulttype;		/* 强制转换的输出类型 */
	/* 输出 typmod 未存储，但假定为 -1 */
	Oid			resultcollid;	/* 排序规则的 OID，如果没有则为 InvalidOid */
	CoercionForm coerceformat;	/* 如何显示这个节点 */
	int			location;		/* 令牌位置，如果未知则为-1 */
} CoerceViaIO;

/* ----------------
 * ArrayCoerceExpr
 *
 * ArrayCoerceExpr 表示从一种数组类型到另一种数组类型的类型强制，
 * 通过将逐元素强制表达式 "elemexpr" 应用到源数组的每个元素来实现。 
 * 在 elemexpr 中，源元素由一个 CaseTestExpr 节点表示。 
 * 请注意，即使 elemexpr 是一个无操作（即，仅为 CaseTestExpr + RelabelType），
 * 强制转换仍然需要一些努力：我们必须修复存储在数组头中的元素类型 OID。
 * ----------------
 */

typedef struct ArrayCoerceExpr
{
	Expr		xpr;
	Expr	   *arg;			/* 输入表达式（产生一个数组） */
	Expr	   *elemexpr;		/* 表示逐元素工作的表达式 */
	Oid			resulttype;		/* 强制转换的输出类型（一个数组类型） */
	int32		resulttypmod;	/* 输出 typmod（也是元素 typmod） */
	Oid			resultcollid;	/* 排序规则的 OID，如果没有则为 InvalidOid */
	CoercionForm coerceformat;	/* 如何显示这个节点 */
	int			location;		/* 令牌位置，如果未知则为-1 */
} ArrayCoerceExpr;

/* ----------------
 * ConvertRowtypeExpr
 *
 * ConvertRowtypeExpr 表示从一种复合类型到另一种复合类型的类型强制，
 * 其中源类型保证包含目标类型所需的所有列，可能还有其他列；
 * 列不需要在相同的位置，但按名称匹配。 
 * 这主要用于将继承子表的全行值转换为其父表的行类型的有效全行值。
 * resulttype 和 "arg" 的暴露类型必须是命名的复合类型（而非域）。
 * ----------------
 */

typedef struct ConvertRowtypeExpr
{
	Expr		xpr;
	Expr	   *arg;			/* 输入表达式 */
	Oid			resulttype;		/* 输出类型（始终是复合类型） */
	/* Like RowExpr, we deliberately omit a typmod and collation here */
	CoercionForm convertformat; /* 如何显示这个节点 */
	int			location;		/* 令牌位置，如果未知则为-1 */
} ConvertRowtypeExpr;

/*----------
 * CollateExpr - COLLATE
 *
 * 规划器在表达式预处理期间将 CollateExpr 替换为 RelabelType，
 * 因此执行时不会看到 CollateExpr。
 *----------
 */
typedef struct CollateExpr
{
	Expr		xpr;
	Expr	   *arg;			/* 输入表达式 */
	Oid			collOid;		/* 排序的 OID */
	int			location;		/* 令牌位置，如果未知则为-1 */
} CollateExpr;

/*----------
 * CaseExpr - 一个 CASE 表达式
 *
 * 我们支持两种不同形式的 CASE 表达式：
 *		CASE WHEN boolexpr THEN expr [ WHEN boolexpr THEN expr ... ]
 *		CASE testexpr WHEN compexpr THEN expr [ WHEN compexpr THEN expr ... ]
 * 这些可以通过 "arg" 字段在第一种情况下为 NULL，而在第二种情况下为 testexpr 来区分。
 *
 * 在第二种形式的原始语法输出中，WHEN 子句的条件表达式仅为比较值。 
 * 解析分析将这些转换为有效的布尔表达式形式
 *		CaseTestExpr '=' compexpr
 * 其中 CaseTestExpr 节点是一个占位符，在运行时发出正确的值。 
 * 这种结构的使用是为了使 testexpr 只需评估一次。
 * 请注意，在解析分析之后，条件表达式始终产生布尔值。
 *
 * 注意：我们可以检查一个 CaseExpr 是否经历了解析分析，
 * 方法是检查 casetype 是否为 InvalidOid。
 *----------
 */
typedef struct CaseExpr
{
	Expr		xpr;
	Oid			casetype;		/* 表达式结果的类型 */
	Oid			casecollid;		/* 排序规则的 OID，如果没有则为 InvalidOid */
	Expr	   *arg;			/* 隐式相等比较参数 */
	List	   *args;			/* 参数（WHEN 子句列表） */
	Expr	   *defresult;		/* 默认结果（ELSE 子句） */
	int			location;		/* 令牌位置，如果未知则为-1 */
} CaseExpr;

/*
 * CaseWhen - CASE 表达式的一条分支
 */
typedef struct CaseWhen
{
	Expr		xpr;
	Expr	   *expr;			/* 条件表达式 */
	Expr	   *result;			/* 替代结果 */
	int			location;		/* 令牌位置，如果未知则为-1 */
} CaseWhen;

/*
 * 用于 CASE 表达式处理的测试值的占位符节点。
 * 这在功能上类似于一个 Param，但可以更简单地实现，
 * 因为我们一次只需要一个替换值。
 *
 * 我们还滥用此节点类型用于其他一些目的，包括：
 *	* ArrayCoerceExpr 中当前数组元素值的占位符；
 *	  请参见 build_coercion_expression()。
 *	* INSERT/UPDATE 中的嵌套 FieldStore/SubscriptingRef 赋值表达式；
 *	  请参见 transformAssignmentIndirection()。
 *
 * CaseExpr 和 ArrayCoerceExpr 中的用法仅在值源节点与其子 CaseTestExpr 之间没有其他 CaseExpr 或 ArrayCoerceExpr 时是安全的。 
 * 在解析分析输出中这是正确的，但规划器的函数内联逻辑必须小心不要破坏它。
 *
 * 嵌套赋值表达式的情况是安全的，因为能在此类 CaseTestExpr 之上的唯一节点类型是 FieldStore 和 SubscriptingRef。
 */
typedef struct CaseTestExpr
{
	Expr		xpr;
	Oid			typeId;			/* 替代值的类型 */
	int32		typeMod;		/* 替代值的 typemod */
	Oid			collation;		/* 替换值的排序规则 */
} CaseTestExpr;

/*
 * ArrayExpr - 一个 ARRAY[] 表达式
 *
 * 注意：如果 multidims 为 false，组成的表达式都返回由 element_typeid 标识的标量类型。如果 multidims 为 true，
 * 组成的表达式都返回 element_typeid 的数组（即与 array_typeid 相同的类型）；在运行时，我们必须检查子脚本是否兼容。
 */
typedef struct ArrayExpr
{
	Expr		xpr;
	Oid			array_typeid;	/* 表达式结果的类型 */
	Oid			array_collid;	/* 排序规则的 OID，如果没有则为 InvalidOid */
	Oid			element_typeid; /* 数组元素的公共类型 */
	List	   *elements;		/* 数组元素或子数组 */
	bool		multidims;		/* 如果元素是子数组，则为true */
	int			location;		/* 令牌位置，如果未知则为-1 */
} ArrayExpr;

/*
 * RowExpr - 一个 ROW() 表达式
 *
 * 注意：字段列表必须与关联的行类型的物理字段有一一对应关系，
 * 尽管它比行类型短是可以的。也就是说，第 N 个列表元素必须
 * 与第 N 个物理字段相匹配。当第 N 个物理字段是被删除的列
 * (attisdropped) 时，第 N 个列表元素可以只是一个 NULL 常量。
 * （这种情况只能发生在命名复合类型上，而不能在 RECORD 类型上，
 * 因为 RECORD 类型是由 RowExpr 本身构建的，而不是反过来。）
 * 重要的是，不要假设 length(args) 与逻辑上存在于行类型中的列数相同。
 *
 * colnames 提供字段名，如果 ROW() 结果是 RECORD 类型。
 * 如果 row_typeid 是 RECORDOID，必须提供名称；但如果它是
 * 命名复合类型，则 colnames 将被忽略，使用类型的目录字段名，
 * 因此 colnames 应该为 NIL。与 args 列表一样，colnames 的
 * 定义与行类型的物理字段是一一对应的（尽管在 RECORD 的情况下
 * 被删除的列不应该出现，因此这一细节目前是无关紧要的）。
 */
typedef struct RowExpr
{
	Expr		xpr;
	List	   *args;			/* 字段 */
	Oid			row_typeid;		/* RECORDOID 或复合类型的 ID */

	/*
	 * row_typeid 不能是复合的域，只能是普通复合。
	 * 要创建复合域值，请对 RowExpr 应用 CoerceToDomain。
	 *
	 * 注意：我们故意不存储 typmod。尽管在运行时会将 typmod 关联
	 * 到特定的 RECORD 类型，但对于不同的后端，它会有所不同，
	 * 因此不能安全地存储在存储的解析树中。我们必须假设
	 * 对于 RowExpr 节点，typmod 为 -1。
	 *
	 * 我们也不需要存储校对。结果类型必然是复合类型，
	 * 而复合类型从来没有校对。
	 */
	CoercionForm row_format;	/* 如何显示这个节点 */
	List	   *colnames;		/* 字符串的列表，或 NIL */
	int			location;		/* 令牌位置，如果未知则为-1 */
} RowExpr;

/*
 * RowCompareExpr - 行比较，例如 (a, b) <= (1, 2)
 *
 * 我们支持任何可以被确定为类似 =, <>, <, <=, >, 或 >= 的操作符的行比较
 * （我们通过查找 btree 操作族中的操作符来确定这一点）。注意，相同的
 * 操作符名称可能会映射到不同的操作符，具体依赖于每一对行元素，
 * 因为元素数据类型可能不同。
 *
 * 仅在 < <= > >= 的情况下生成 RowCompareExpr 节点；
 * = 和 <> 的情况被转换为一对一比较的简单 AND 或 OR 组合。
 * 然而，为了方便解析器逻辑，我们在 RowCompareType 枚举中
 * 包括 = 和 <> 。
 */
typedef enum RowCompareType
{
	/* 此枚举的值选择以匹配 btree 策略数字 */
	ROWCOMPARE_LT = 1,			/* BTLessStrategyNumber */
	ROWCOMPARE_LE = 2,			/* BTLessEqualStrategyNumber */
	ROWCOMPARE_EQ = 3,			/* BTEqualStrategyNumber */
	ROWCOMPARE_GE = 4,			/* BTGreaterEqualStrategyNumber */
	ROWCOMPARE_GT = 5,			/* BTGreaterStrategyNumber */
	ROWCOMPARE_NE = 6			/* 没有这样的 btree 策略 */
} RowCompareType;

typedef struct RowCompareExpr
{
	Expr		xpr;
	RowCompareType rctype;		/* LT LE GE 或 GT，从不 EQ 或 NE */
	List	   *opnos;			/* 成对比较操作的 OID 列表 */
	List	   *opfamilies;		/* 包含操作族的 OID 列表 */
	List	   *inputcollids;	/* 比较的校对的 OID 列表 */
	List	   *largs;			/* 左侧输入参数 */
	List	   *rargs;			/* 右侧输入参数 */
} RowCompareExpr;

/*
 * CoalesceExpr - COALESCE 表达式
 */
typedef struct CoalesceExpr
{
	Expr		xpr;
	Oid			coalescetype;	/* 表达式结果的类型 */
	Oid			coalescecollid; /* 排序规则的 OID，如果没有则为 InvalidOid */
	List	   *args;			/* 参数 */
	int			location;		/* 令牌位置，如果未知则为-1 */
} CoalesceExpr;

/*
 * MinMaxExpr - GREATEST 或 LEAST 函数
 */
typedef enum MinMaxOp
{
	IS_GREATEST,
	IS_LEAST
} MinMaxOp;

typedef struct MinMaxExpr
{
	Expr		xpr;
	Oid			minmaxtype;		/* 参数和结果的常见类型 */
	Oid			minmaxcollid;	/* 结果的排序规则的OID */
	Oid			inputcollid;	/* 函数应使用的排序规则的OID */
	MinMaxOp	op;				/* 要执行的函数 */
	List	   *args;			/* 参数 */
	int			location;		/* 令牌位置，如果未知则为-1 */
} MinMaxExpr;

/*
 * SQLValueFunction - 没有参数的具有特殊语法规则的函数
 *
 * SQL 标准将其中一些分类为 <datetime value function>，
 * 其他则为 <general value specification>。我们称它们为 SQLValueFunctions，
 * 因为没有更好的术语。我们存储结果的类型和 typmod，以便
 * 某些代码不需要了解每个函数，并且因为对于某些日期时间函数
 * 我们无论如何需要存储 typmod。注意，目前，所有变体返回非校对
 * 数据类型，因此我们不需要持有校对字段；另外，所有这些函数都是稳定的。
 */
typedef enum SQLValueFunctionOp
{
	SVFOP_CURRENT_DATE,
	SVFOP_CURRENT_TIME,
	SVFOP_CURRENT_TIME_N,
	SVFOP_CURRENT_TIMESTAMP,
	SVFOP_CURRENT_TIMESTAMP_N,
	SVFOP_LOCALTIME,
	SVFOP_LOCALTIME_N,
	SVFOP_LOCALTIMESTAMP,
	SVFOP_LOCALTIMESTAMP_N,
	SVFOP_CURRENT_ROLE,
	SVFOP_CURRENT_USER,
	SVFOP_USER,
	SVFOP_SESSION_USER,
	SVFOP_CURRENT_CATALOG,
	SVFOP_CURRENT_SCHEMA
} SQLValueFunctionOp;

typedef struct SQLValueFunction
{
	Expr		xpr;
	SQLValueFunctionOp op;		/* 这是哪个函数 */
	Oid			type;			/* 结果类型/typmod */
	int32		typmod;
	int			location;		/* 令牌位置，如果未知则为-1 */
} SQLValueFunction;

/*
 * XmlExpr - 各种 SQL/XML 函数需要特殊的语法规则
 *
 * 'name' 传递 "NAME foo" 参数（已经进行 XML 转义）。
 * 'named_args' 和 'arg_names' 表示一个 xml_attribute 列表。
 * 'args' 传递所有其他参数。
 *
 * 注意：结果类型/typmod/校对未被存储，但可以从 XmlExprOp 中推导
 * 出来。类型/typmod 字段仅用于显示目的，并不一定是节点的真实结果类型。
 */
typedef enum XmlExprOp
{
	IS_XMLCONCAT,				/* XMLCONCAT(args) */
	IS_XMLELEMENT,				/* XMLELEMENT(name, xml_attributes, args) */
	IS_XMLFOREST,				/* XMLFOREST(xml_attributes) */
	IS_XMLPARSE,				/* XMLPARSE(text, is_doc, preserve_ws) */
	IS_XMLPI,					/* XMLPI(name [, args]) */
	IS_XMLROOT,					/* XMLROOT(xml, version, standalone) */
	IS_XMLSERIALIZE,			/* XMLSERIALIZE(is_document, xmlval) */
	IS_DOCUMENT					/* xmlval 是文档 */
} XmlExprOp;

typedef enum XmlOptionType
{
	XMLOPTION_DOCUMENT,
	XMLOPTION_CONTENT
} XmlOptionType;

typedef struct XmlExpr
{
	Expr		xpr;
	XmlExprOp	op;				/* xml 函数 ID */
	char	   *name;			/* xml 中的名称（名称 foo ...）语法 */
	List	   *named_args;		/* xml_attributes 的非 XML 表达式 */
	List	   *arg_names;		/* 字符串值的平行列表 */
	List	   *args;			/* 表达式列表 */
	XmlOptionType xmloption;	/* DOCUMENT 或 CONTENT */
	Oid			type;			/* XMLSERIALIZE 的目标类型/typmod */
	int32		typmod;
	int			location;		/* 令牌位置，如果未知则为-1 */
} XmlExpr;

/* ----------------
 * NullTest
 *
 * NullTest 表示测试一个值是否为 NULL 的操作。
 * 进行适当的测试并返回一个布尔 Datum。
 *
 * 当 argisrow 为 false 时，这只是表示对 null 值的测试。
 *
 * 当 argisrow 为 true 时，输入表达式必须产生一个行类型，并且
 * 该节点实现 "row IS [NOT] NULL" 按照 SQL 标准。这
 * 包括在行数据本身不是 NULL 时检查单个字段是否为 NULL。
 *
 * 注意：行类型输入与 argisrow==false 的组合并不
 * 对应 SQL 笔记 “row IS [NOT] NULL”；相反，此情况
 * 表示 SQL 笔记 “row IS [NOT] DISTINCT FROM NULL”。
 * ----------------
 */

typedef enum NullTestType
{
	IS_NULL, IS_NOT_NULL
} NullTestType;

typedef struct NullTest
{
	Expr		xpr;
	Expr	   *arg;			/* 输入表达式 */
	NullTestType nulltesttype;	/* IS NULL, IS NOT NULL */
	bool		argisrow;		/* T 以执行逐字段的空检查 */
	int			location;		/* 令牌位置，如果未知则为-1 */
} NullTest;

/*
 * BooleanTest
 *
 * BooleanTest 表示确定布尔值是否为 TRUE、FALSE 或 UNKNOWN（即 NULL）的操作。
 * 支持所有六种有意义的组合。注意 NULL 输入并不会导致 NULL 结果。
 * 进行适当的测试并返回一个布尔 Datum。
 */

typedef enum BoolTestType
{
	IS_TRUE, IS_NOT_TRUE, IS_FALSE, IS_NOT_FALSE, IS_UNKNOWN, IS_NOT_UNKNOWN
} BoolTestType;

typedef struct BooleanTest
{
	Expr		xpr;
	Expr	   *arg;			/* 输入表达式 */
	BoolTestType booltesttype;	/* 测试类型 */
	int			location;		/* 令牌位置，如果未知则为-1 */
} BooleanTest;

/*
 * CoerceToDomain
 *
 * CoerceToDomain 表示将值强制转换为域类型的操作。
 * 在运行时（而不是之前）将确定要检查的约束的精确集合。
 * 如果值通过，则作为结果返回；否则，引发错误。注意，这在没有应用约束的情况下等同于
 * RelabelType。
 */
typedef struct CoerceToDomain
{
	Expr		xpr;
	Expr	   *arg;			/* 输入表达式 */
	Oid			resulttype;		/* 域类型 ID（结果类型） */
	int32		resulttypmod;	/* 输出 typmod（当前始终为 -1） */
	Oid			resultcollid;	/* 排序规则的 OID，如果没有则为 InvalidOid */
	CoercionForm coercionformat;	/* 如何显示这个节点 */
	int			location;		/* 令牌位置，如果未知则为-1 */
} CoerceToDomain;

/*
 * 用于处理域的检查约束的占位符节点。
 * 这实际上就像一个 Param，但可以更简单地实现，因为我们每次只需要一个替换值。
 *
 * 注意：typeId/typeMod/collation 将从域的基础类型设置，而不是域本身。
 * 这是因为如果我们尚未检查其约束，则不应认为该值属于该域。
 */
typedef struct CoerceToDomainValue
{
	Expr		xpr;
	Oid			typeId;			/* 替代值的类型 */
	int32		typeMod;		/* 替代值的 typemod */
	Oid			collation;		/* 替换值的排序规则 */
	int			location;		/* 令牌位置，如果未知则为-1 */
} CoerceToDomainValue;

/*
 * INSERT 或 UPDATE 命令中的 DEFAULT 标记的占位符节点。
 *
 * 这不是可执行的表达式：在重写过程中必须替换为实际
 * 列的默认表达式。但在解析和重写期间将其视为
 * 表达式节点是方便的。
 */
typedef struct SetToDefault
{
	Expr		xpr;
	Oid			typeId;			/* 替代值的类型 */
	int32		typeMod;		/* 替代值的 typemod */
	Oid			collation;		/* 替换值的排序规则 */
	int			location;		/* 令牌位置，如果未知则为-1 */
} SetToDefault;

/*
 * 表示 [WHERE] CURRENT OF cursor_name 的节点
 *
 * CURRENT OF 有点像 Var，因为它携带目标关系的 rangetable 索引；
 * 这有助于在规划时正确放置表达式。然而，我们可以假设它的 "levelsup"
 * 始终为零，因为对其出现位置的语法约束。此外，cvarno 将始终是真实的
 * RT 索引，而不是 INNER_VAR 等等。
 *
 * 被引用的游标可以表示为一个硬编码的字符串
 * 或作为类型为 REFCURSOR 的运行时参数的引用。
 * 后者的情况是为 plpgsql 的便利。
 */
typedef struct CurrentOfExpr
{
	Expr		xpr;
	Index		cvarno;			/* 目标关系的 RT 索引 */
	char	   *cursor_name;	/* 被引用游标的名称，或 NULL */
	int			cursor_param;	/* refcursor 参数编号，或 0 */
} CurrentOfExpr;

/*
 * NextValueExpr - 从序列中获取下一个值
 *
 * 这与调用 nextval() 函数的效果相同，但它不
 * 检查序列上的权限。这用于身份列，
 * 其中序列是一个隐式依赖，没有自己的权限。
 */
typedef struct NextValueExpr
{
	Expr		xpr;
	Oid			seqid;
	Oid			typeId;
} NextValueExpr;

/*
 * InferenceElem - 唯一索引推导规范的一个元素
 *
 * 这大致匹配 IndexElems 的结构，但拥有专用的
 * primnode 允许在实用命令使用索引参数与此节点之间进行干净的分离。
 */
typedef struct InferenceElem
{
	Expr		xpr;
	Node	   *expr;			/* 要推断的表达式，或 NULL */
	Oid			infercollid;	/* collation 的 OID，或 InvalidOid */
	Oid			inferopclass;	/* 属性操作类的 OID，或 InvalidOid */
} InferenceElem;


/*--------------------
 * TargetEntry -
 *	   目标条目（用于查询目标列表）
 *
 * 严格来说，目标条目不是一个表达式节点（因为它不能
 * 被 ExecEvalExpr 评估）。但我们仍然将其视为一个，因为在
 * 很多地方，将整个查询目标列表作为一个单一的表达式树来
 * 处理是非常方便的。
 *
 * 在 SELECT 的目标列表中，resno 应始终等于项的
 * 序号（从 1 开始计数）。然而，在 INSERT 或 UPDATE
 * 目标列表中，resno 表示目标列的属性编号；因此可能会
 * 有缺失或无序的 resnos。甚至出现重复的 resnos 也是合法的；
 * 例如：
 *		UPDATE table SET arraycol[1] = ..., arraycol[2] = ..., ...
 * 在 INSERT 中，重写器和规划器将通过
 * 重新排序为物理列顺序并为原始查询未分配值的任何列填充
 * 默认值来规范化目标列表。在 UPDATE 中，
 * 在重写器合并同一列的多个赋值后，规划器将目标列编号
 * 提取到一个单独的 "update_colnos"
 * 列表中，然后串行重新编号目标列表元素。因此，目标列表中的
 * resnos 在执行器看到的所有目标列表中都与序号位置相匹配；
 * 但在计划发生之前假设这一点是错误的。
 *
 * resname 在非 resjunk 的顶层 SELECT 目标列表条目中
 * 需要表示正确的列名称，因为它将用于发送到前端的
 * 列标题。在大多数其他上下文中，它只是一个调试辅助工具，
 * 可能是错误的甚至为空。（特别是，如果引用的列在
 * 规则制定后通过 ALTER TABLE 被重命名，则在存储规则的
 * 目标列表中可能是错误的。此外，规划器倾向于存储 NULL
 * 而不是查找有效名称，用于非顶层计划节点中的目标列表条目。）
 * 在 resjunk 条目中，resname 应该是一个
 * 特定的系统生成名称（例如 "ctid"）或 NULL；其他任何值
 * 都可能会导致 ExecGetJunkAttribute 的混淆！
 *
 * ressortgroupref 用于表示 ORDER BY、GROUP BY 和
 * DISTINCT 项。 ressortgroupref=0 的目标列表条目
 * 不是排序/分组项。如果 ressortgroupref>0，则该项是
 * ORDER BY、GROUP BY 和/或 DISTINCT 的目标值。
 * 目标列表中不能有两个条目具有相同的非零 ressortgroupref，
 * 但非零值没有特别的含义，除了作为标签。（例如，不能假设
 * 较低的 ressortgroupref 意味着更重要的排序键。）
 * 关联的 SortGroupClause 列表的顺序决定语义。
 *
 * resorigtbl/resorigcol 识别列的来源，如果它是一个
 * 对基础表（或视图）列的简单引用。如果不是简单引用，
 * 则这些字段为零。
 *
 * 如果 resjunk 为真，则该列是一个工作列（例如排序键），
 * 应从查询的最终输出中移除。 resjunk 列必须具有无法
 * 重复任何常规列的 resno。同时请注意，有些地方
 * 假设 resjunk 列在非垃圾列之后。
 *--------------------
 */
typedef struct TargetEntry
{
	Expr		xpr;
	Expr	   *expr;			/* expression to evaluate */
	AttrNumber	resno;			/* 属性编号（见上面的说明） */
	char	   *resname;		/* 列的名称（可能为 NULL） */
	Index		ressortgroupref;	/* 如果在排序/分组中引用，则为非零 */
	Oid			resorigtbl;		/* 列的源表的 OID */
	AttrNumber	resorigcol;		/* 列在源表中的编号 */
	bool		resjunk;		/* 设置为 true 以从最终目标列表中消除属性 */
} TargetEntry;


/* ----------------------------------------------------------------
 *					连接树的节点类型
 *
 * 连接树结构的叶子是 RangeTblRef 节点。在这些节点上方，
 * JoinExpr 节点可以出现以表示特定类型的连接或合格连接。
 * 此外，FromExpr 节点可以出现以表示普通的交叉乘积连接
 * （“FROM foo, bar, baz WHERE ...”）。FromExpr 类似于
 * jointype 为 JOIN_INNER 的 JoinExpr，但可以具有任何数量
 * 的子节点，而不仅仅是两个。
 *
 * 注意：查询的连接树的顶层总是一个 FromExpr。
 * 即使连接树不包含任何关系，也会有一个 FromExpr。
 *
 * 注意：存在于 JoinExpr 节点中的资格表达式是
 * *额外的* 查询的主要 WHERE 子句，后者作为
 * 顶层 FromExpr 的 qual 出现。将资格与连接树中特定节点的
 * 关联是因为当存在外连接时，qual 的位置至关重要。
 * （如果我们过早或过晚地强制执行 qual，
 * 那可能导致外连接产生错误的 NULL 扩展行集合。）
 * 如果所有连接都是内连接，则所有 qual 位置在语义上
 * 是可以互换的。
 *
 * 注意：在 gram.y 的原始输出中，连接树包含 RangeVar、
 * RangeSubselect 和 RangeFunction 节点，这些节点在解析分析阶段
 * 被 RangeTblRef 节点替换。此外，顶层 FromExpr 在解析分析
 * 期间添加；语法将 FROM 和 WHERE 视为分开的。
 * ----------------------------------------------------------------
 */

/*
 * RangeTblRef - 查询范围表中条目的引用
 *
 * 我们可以使用对 RT 条目的直接指针，而跳过这些
 * 节点，但对查询树中同一节点的多个指针会造成
 * 很多麻烦，因此将其存储为 RT 的索引似乎更好。
 */
typedef struct RangeTblRef
{
	NodeTag		type;
	int			rtindex;
} RangeTblRef;

/*----------
 * JoinExpr - 针对 SQL JOIN 表达式
 *
 * isNatural、usingClause 和 quals 是相互依赖的。用户只能
 * 编写 NATURAL、USING() 或 ON() 中的一个（这由语法强制执行）。
 * 如果他编写 NATURAL，则解析分析会生成等效的 USING()
 * 列表，并从中用正确的相等比较填充“quals”。如果他编写
 * USING()，则“quals”会填充相等比较。如果他编写 ON()，
 * 则仅设置“quals”。注意，NATURAL/USING 不等同于 ON()
 * ，因为它们还会影响输出列列表。
 *
 * alias 是一个 Alias 节点，表示附加到连接表达式的
 * AS 别名子句，或者如果没有子句则为 NULL。注意：别名的
 * 存在与否对语义有重要影响，因为具有别名的连接会限制
 * 内部表/列的可见性。
 *
 * join_using_alias 是一个 Alias 节点，表示 SQL:2016
 * 及之后版本允许附加到 JOIN/USING 的连接相关名称。
 * 其列别名列表仅包括来自 USING 的公共列名称，并且不
 * 限制连接输入表的可见性。
 *
 * 在解析分析期间，为 Join 创建一个 RTE，并将其索引
 * 填充到 rtindex 中。此 RTE 主要存在是为了能够创建
 * 引用连接输出的 Vars。计划程序有时会在内部生成
 * JoinExpr；如果没有引用这些连接的连接别名变量，这些
 * 可以具有 rtindex = 0。
 *----------
 */
typedef struct JoinExpr
{
	NodeTag		type;
	JoinType	jointype;		/* 联接类型 */
	bool		isNatural;		/* 自然连接？需要塑形表 */
	Node	   *larg;			/* 左子树 */
	Node	   *rarg;			/* 右子树 */
	List	   *usingClause;	/* USING 子句，如果有的话（字符串列表） */
	Alias	   *join_using_alias;	/* 附加到 USING 子句的别名（如果有） */
	Node	   *quals;			/* 连接上的限定符（如果有） */
	Alias	   *alias;			/* 用户编写的别名子句（如果有） */
	int			rtindex;		/* 为连接分配的 RT 索引，或 0 */
} JoinExpr;

/*----------
 * FromExpr - 表示一个 FROM ... WHERE ... 结构
 *
 * 这比 JoinExpr 更灵活（可以有任何数量的子节点，
 * 包括零），但也不那么灵活——我们不需要处理
 * 别名等等。输出列集隐式地是子节点输出的并集。
 *----------
 */
typedef struct FromExpr
{
	NodeTag		type;
	List	   *fromlist;		/* 连接子树列表 */
	Node	   *quals;			/* 连接上的限定符（如果有） */
} FromExpr;

/*----------
 * OnConflictExpr - 表示 ON CONFLICT DO ... 表达式
 *
 * 优化器需要一组推断元素的列表，并可选择提供用于
 * 推断唯一索引的 WHERE 子句。推断的唯一索引（或偶尔
 * 的索引）用于判断是否采用替代的 ON CONFLICT 路径。
 *----------
 */
typedef struct OnConflictExpr
{
	NodeTag		type;
	OnConflictAction action;	/* DO NOTHING 或 UPDATE？ */

	/* Arbiter */
	List	   *arbiterElems;	/* 唯一索引仲裁者列表（
								 * InferenceElem 的） */
	Node	   *arbiterWhere;	/* 唯一索引仲裁者 WHERE 子句 */
	Oid			constraint;		/* 用于仲裁的 pg_constraint OID */

	/* ON CONFLICT UPDATE */
	List	   *onConflictSet;	
/* ON CONFLICT SET TargetEntrys 的列表 */
	Node	   *onConflictWhere;	/* 限制 UPDATE 的修饰符 */
	int			exclRelIndex;	/* 'excluded' 关系的 RT 索引 */
	List	   *exclRelTlist;	/* EXCLUDED 伪关系的 tlist */
} OnConflictExpr;

#endif							/* PRIMNODES_H */
